package com.googlecode.kipler.syntax.concept;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

import com.googlecode.kipler.common.Copyable;

public class NaryBooleanConcept extends Concept {

	/**
	 * The binary constructor used in this concept expression
	 */
	private BooleanConstructor constructor;

	private Set<Concept> operands = new LinkedHashSet<Concept>();

	/**
	 * Default constructor
	 */
	public NaryBooleanConcept() {

	}

	public NaryBooleanConcept(BooleanConstructor constructor) {
		setConstructor(constructor);
	}

	/**
	 * @return the binary constructor used in this concept expression.
	 */
	public BooleanConstructor getConstructor() {
		return constructor;
	}

	/**
	 * Sets the binary constructor used in this concept expression.
	 * 
	 * @param constructor
	 *            The binary constructor
	 */
	public void setConstructor(BooleanConstructor constructor) {
		this.constructor = constructor;
	}

	public void addOperand(Concept c) {
		operands.add(c);
	}

	public Set<Concept> getOperands() {
		return operands;
	}

	/**
	 * @return false
	 */
	public boolean isNegated() {
		return false;
	}

	/**
	 * Changes the expression to union if it's an intersection or vice versa.
	 */
	public Concept toggleNegated() {
		if (getConstructor() == BooleanConstructor.INTERSECTION) {
			setConstructor(BooleanConstructor.UNION);
		} else {
			setConstructor(BooleanConstructor.INTERSECTION);
		}
		for (Concept c : operands) {
			c.toggleNegated();
		}
		return this;
	}

	/**
	 * @return true if both concepts have the same constructor and operands.
	 *         Operand order does not matter in the equality check.
	 */
	@Override
	public boolean equals(Object obj) {
		if (obj != null && (obj.getClass().equals(this.getClass()))) {
			NaryBooleanConcept other = (NaryBooleanConcept) obj;
			return (this.isLeftBiased() == other.isLeftBiased()
					&& this.getConstructor() == other.getConstructor() && operands
					.equals(other.operands));
		}
		return false;
	}

	/**
	 * @see tr.edu.ege.seagent.alcbi.syntax.description.Concept#hashCode()
	 */
	@Override
	public int hashCode() {
		return 17 * 37 + new Boolean(isLeftBiased()).hashCode()
				+ getConstructor().hashCode() + getOperands().hashCode();
	}

	public void accept(ConceptVisitor v) {
		v.visitNaryBooleanConcept(this);
	}

	/**
	 * @see Copyable#copy()
	 */
	public NaryBooleanConcept copy() {
		NaryBooleanConcept bd = new NaryBooleanConcept();
		bd.setConstructor(getConstructor());
		bd.operands = new LinkedHashSet<Concept>(operands);
		bd.setLeftBiased(isLeftBiased());
		return bd;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		buffer.append("(");
		String op = getConstructor() == BooleanConstructor.INTERSECTION ? " & "
				: " | ";
		for (Concept c : operands) {
			buffer.append(c);
			buffer.append(op);
		}
		buffer.delete(buffer.length() - 3, buffer.length());
		buffer.append(")");
		return buffer.toString();
	}

	@Override
	public void setLeftBiased(boolean value) {
		super.setLeftBiased(value);
		for (Concept c : operands) {
			c.setLeftBiased(value);
		}
	}

	public Concept toBinary() {
		Iterator<Concept> i = getOperands().iterator();
		Concept lhs = i.next();
		Concept rhs = i.next();
		Concept current = new BooleanConcept(getConstructor(), lhs, rhs);
		while (i.hasNext()) {
			current = new BooleanConcept(getConstructor(), current, i.next());
		}
		return current;
	}

}
